﻿using System;
using System.Runtime.CompilerServices;

namespace LINQPadLogParser
{
  public sealed class UriWrapper : IFormattable
  {
    private readonly int _id;
    private string _host;
    private string _serverIP;
    private string _serverPort;
    private Uri _uri;
    private string _uriQuery;
    private string _uriStem;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public UriWrapper(int id)
    {
      _id = id;
      _uri = null;
    }

    private string HostString
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _host ?? (_host = LogParserDataContext.LineManager.GetValue(_id, W3CColumn.Host)); }
    }

    private string ServerIPString
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)]
      get
      {
        return _serverIP ?? (_serverIP = LogParserDataContext.LineManager.GetValue(_id, W3CColumn.ServerIP));
      }
    }

    private string ServerPortString
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)]
      get
      {
        return _serverPort ?? (_serverPort = LogParserDataContext.LineManager.GetValue(_id, W3CColumn.ServerPort));
      }
    }

    private string UriStemString
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)]
      get
      {
        return _uriStem ?? (_uriStem = LogParserDataContext.LineManager.GetValue(_id, W3CColumn.UriStem));
      }
    }

    private string UriQueryString
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)]
      get
      {
        return _uriQuery ?? (_uriQuery = LogParserDataContext.LineManager.GetValue(_id, W3CColumn.UriQuery));
      }
    }

    private Uri Uri
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)]
      get
      {
        if (_uri != null)
          return _uri;
        MultipleLineManager manager = LogParserDataContext.LineManager;
        string host = HostString ?? ServerIPString;
        Uri uri;
        string prefix;
        if (host != null)
        {
          string port = ServerPortString;
          prefix = port == null ? null : ":";
          uri = new Uri("http://" + host + prefix + port);
        }
        else
        {
          uri = manager.Uri;
        }
        string path = UriStemString;
        string query = UriQueryString;
        prefix = query == null ? null : "?";
        uri = new Uri(uri, path + prefix + query);
        _uri = uri;
        return uri;
      }
    }

    public string AbsolutePath
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _uri == null ? UriStemString : _uri.AbsolutePath; }
    }

    public string AbsoluteUri
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Uri.AbsoluteUri; }
    }

    public string LocalPath
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Uri.LocalPath; }
    }

    public string Authority
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Uri.Authority; }
    }

    public UriHostNameType HostNameType
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Uri.HostNameType; }
    }

    public bool IsDefaultPort
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Uri.IsDefaultPort; }
    }

    public bool IsFile
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Uri.IsFile; }
    }

    public bool IsLoopback
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Uri.IsLoopback; }
    }

    public string PathAndQuery
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)]
      get
      {
        if (_uri != null)
          return _uri.PathAndQuery;
        string path = UriStemString;
        if (path == null)
          return null;
        string query = UriQueryString;
        string prefix = query == null ? null : "?";
        return path + prefix + query;
      }
    }

    public string[] Segments
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Uri.Segments; }
    }

    public bool IsUnc
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Uri.IsUnc; }
    }

    public string Host
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)]
      get
      {
        if (_uri != null)
          return _uri.Host;
        MultipleLineManager manager = LogParserDataContext.LineManager;
        return HostString ?? ServerIPString ?? manager.Uri.Host;
      }
    }

    public int Port
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)]
      get
      {
        if (_uri != null)
        {
          return _uri.Port;
        }
        MultipleLineManager manager = LogParserDataContext.LineManager;
        string port = ServerPortString;
        if (port != null)
          return int.Parse(port);
        return manager.Uri.Port;
      }
    }

    public string Query
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)]
      get
      {
        if (_uri != null)
          return _uri.Query;
        string query = UriQueryString;
        return query == null ? null : "?" + query;
      }
    }

    public string Fragment
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Uri.Fragment; }
    }

    public string Scheme
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Uri.Scheme; }
    }

    public string OriginalString
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Uri.OriginalString; }
    }

    public string DnsSafeHost
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Uri.DnsSafeHost; }
    }

    public bool IsAbsoluteUri
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Uri.IsAbsoluteUri; }
    }

    public bool UserEscaped
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Uri.UserEscaped; }
    }

    public string UserInfo
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Uri.UserInfo; }
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public string ToString(string format, IFormatProvider formatProvider)
    {
      return ToString();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public override string ToString()
    {
      return Uri.ToString();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public string GetLeftPart(UriPartial part)
    {
      return Uri.GetLeftPart(part);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public Uri MakeRelativeUri(Uri uri)
    {
      return Uri.MakeRelativeUri(uri);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public string MakeRelative(Uri toUri)
    {
// ReSharper disable once CSharpWarnings::CS0618
      return Uri.MakeRelative(toUri);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public string GetComponents(UriComponents components, UriFormat format)
    {
      return Uri.GetComponents(components, format);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool IsWellFormedOriginalString()
    {
      return Uri.IsWellFormedOriginalString();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool IsBaseOf(Uri uri)
    {
      return Uri.IsBaseOf(uri);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static implicit operator Uri(UriWrapper wrapper)
    {
      return wrapper.Uri;
    }
  }
}